use core::{cell::UnsafeCell, ffi::c_void, ptr::null_mut};

use crate::{
    c::rtx_os::{
        osPriority_t_osPriorityHigh, osPriority_t_osPriorityHigh1, osPriority_t_osPriorityHigh2,
        osRtxThread_t, osThreadAttr_t, osThreadDetached,
        osThreadId_t, osThreadJoinable, osThreadNew,
    },
    utils::AlignStore,
};

const CB_SIZE: usize = size_of::<osRtxThread_t>();

pub const PRIORITY_HIGH: i32 = osPriority_t_osPriorityHigh;
pub const PRIORITY_HIGH1: i32 = osPriority_t_osPriorityHigh1;
pub const PRIORITY_HIGH2: i32 = osPriority_t_osPriorityHigh2;

pub const THREAD_DETACHED: u32 = osThreadDetached;
pub const THREAD_JOINABLE: u32 = osThreadJoinable;

unsafe impl<const STACK_SIZE: usize, const PRIORITY: i32, const ATTR: u32> Sync for Thread<STACK_SIZE, PRIORITY, ATTR> {}
unsafe impl<const STACK_SIZE: usize, const PRIORITY: i32, const ATTR: u32> Send for Thread<STACK_SIZE, PRIORITY, ATTR> {}

pub struct Thread<const STACK_SIZE: usize, const PRIORITY: i32, const ATTR: u32> {
    cb_mem: AlignStore<CB_SIZE>,
    stack_mem: AlignStore<STACK_SIZE>,
    id: UnsafeCell<osThreadId_t>,
}

impl<const STACK_SIZE: usize, const PRIORITY: i32, const ATTR: u32>
    Thread<STACK_SIZE, PRIORITY, ATTR>
{
    pub const fn const_new() -> Self {
        Self {
            cb_mem: AlignStore::const_new(),
            stack_mem: AlignStore::const_new(),
            id: UnsafeCell::new(null_mut()),
        }
    }

    pub unsafe fn create(&self, name: &str, func: extern "C" fn(_: *mut c_void), arg: *mut c_void) {
        let attr = osThreadAttr_t {
            name: name.as_ptr() as *const i8,
            attr_bits: ATTR,
            cb_mem: self.cb_mem.raw_ptr(),
            cb_size: CB_SIZE as u32,
            stack_mem: self.stack_mem.raw_ptr(),
            stack_size: STACK_SIZE as u32,
            priority: PRIORITY,
            tz_module: 0,
            reserved: 0,
        };
        let id = osThreadNew(Some(func), arg, &attr);
        #[cfg(feature = "assert")]
        assert!(id != null_mut(), "thread create fail");
        *self.id.get() = id;
    }
}
